#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
#include "GenericRow.h"

using namespace std;
////////////////////////////////////////////////////
///// Auxiliar FUNCTIONS  /////
////////////////////////////////////////////////////

int comparar(const void* _a, const void* _b) {
    // you've got to explicitly cast to the correct type
    const float* a = (const float*) _a;
    const float* b = (const float*) _b;

    if (*a > *b) return 1; // first item is bigger than the second one -> return 1
    else
        if (*a == *b) return 0; // equality -> return 0
    else return -1; // second item is bigger than the first one -> return -1
}
////////////////////////////////////////////////////
///// Start of Histograma class  /////
////////////////////////////////////////////////////

class Histograma : public AppData {
private:
    // AppData variables
    int size_;
    char* str_;

    //Specific
    vector <GenericRow> bin_array_;
    float max_;
    float min_;
    float binWidth_;
    float sum_;
    int nBins_;
    int nleituras_;
    int freq_prod_;

    void transforma(vector<float> dados_sensor) {
        vector<GenericRow>::iterator it;
        GenericRow aux;
        int diferentes = 1;

        sort(dados_sensor.begin(), dados_sensor.end());
        //qsort(dados_sensor, size, sizeof (float), comparar);

        min_ = dados_sensor.at(0);

        max_ = dados_sensor.at((int) dados_sensor.size() - 1);

        for (int i = 1; i < (int) dados_sensor.size(); i++) {
            if (dados_sensor.at(i - 1) != dados_sensor.at(i))
                diferentes++;
        }

        if (diferentes < nBins_)
            nBins_ = diferentes;

        bin_array_ = *new vector<GenericRow > (nBins_, *new GenericRow());

        //        for (it = bin_array_.begin(); it < bin_array_.end(); it++)
        //            *it = 0;

        binWidth_ = (max_ - min_) / nBins_;
        int pos = 0;
        float limit = min_;

        for (it = bin_array_.begin(); it < bin_array_.end(); it++) {
            aux = *it;
            aux.SetPosicao(pos);
            aux.SetInicio(limit);
            limit += binWidth_;
            aux.SetFim(limit);
            aux.SetResponsavel(NULL);
            aux.SetOcorrencias(0);
            aux.SetHistoType();
            bin_array_.at(pos) = aux;
            pos++;
        }
        bin_array_.at(pos-1).SetFim(max_);

        for (int i = 0; i < (int) dados_sensor.size(); i++) {
            for (it = bin_array_.begin(); it < bin_array_.end(); it++) {
                aux = *it;
                if (aux.checkRange(dados_sensor.at(i)))
                    aux.IncOcorrencias();
                bin_array_.at(aux.GetPosicao()) = aux;
            }
            sum_ += dados_sensor.at(i);
        }

        //            for (int i = 0; i < (int) dados_sensor.size(); i++) {
        //                if (binWidth_ == 0)
        //                    bin = 0;
        //                else
        //                    bin = floor((dados_sensor.at(i) - min_) / binWidth_);
        //                bin != (int) bin_array_.size() ? : bin--;
        //                //printf("BIN: %d  binWidth: %f dados_sensor[%d]: %f \n",bin,binWidth_,i,dados_sensor->at(i));
        //                bin_array_.at(bin)++;
        //                sum_ += dados_sensor.at(i);
        //
        //            }

    }

    void transforma(vector<float> dados_sensor, vector <GenericRow> vect) {
        vector<GenericRow>::iterator it;
        GenericRow aux;
        sort(dados_sensor.begin(), dados_sensor.end());

        min_ = dados_sensor.at(0);

        max_ = dados_sensor.at((int) dados_sensor.size() - 1);
        bin_array_ = *new vector<GenericRow> (vect);
//        for (int i = 0; i < bin_array_.size(); i++)
//            bin_array_.at(i).SetOcorrencias(0);
        for (int i = 0; i < (int) dados_sensor.size(); i++) {
            for (it = bin_array_.begin(); it < bin_array_.end(); it++) {
                aux = *it;
                aux.SetHistoType();
                if (aux.checkRange(dados_sensor.at(i)))
                    aux.IncOcorrencias();
                bin_array_.at(aux.GetPosicao()) = aux;
            }
            sum_ += dados_sensor.at(i);
        }
    }



public:

    Histograma() : AppData(MSCOOP_DATA), size_(0), str_(NULL) {
        max_ = 0;
        min_ = 0;
        binWidth_ = 0;
        sum_ = 0;
        nBins_ = 10;
    }

    Histograma(vector<float> dados_sensor, int nbins) : AppData(MSCOOP_DATA), size_(0), str_(NULL) {
        max_ = 0;
        min_ = 0;
        binWidth_ = 0;
        sum_ = 0;
        nBins_ = nbins;
        nleituras_ = dados_sensor.size();
        transforma(dados_sensor);
    }

    Histograma(vector<float> dados_sensor, vector <GenericRow> vect) : AppData(MSCOOP_DATA), size_(0), str_(NULL) {
        max_ = 0;
        min_ = 0;
        binWidth_ = 0;
        sum_ = 0;
        nBins_ = (int) vect.size();
        nleituras_ = dados_sensor.size();
        transforma(dados_sensor, vect);
    }

    Histograma(const Histograma & data) : AppData(MSCOOP_DATA) {
        bin_array_ = data.bin_array_;
        max_ = data.max_;
        min_ = data.min_;
        binWidth_ = data.binWidth_;
        sum_ = data.sum_;
        nBins_ = data.nBins_;
        nleituras_ = data.nleituras_;
        size_ = data.size_;
        freq_prod_ = data.freq_prod_;
        if (size_ > 0) {
            str_ = new char[size_];
            strcpy(str_, data.str_);
        } else str_ = NULL;
    }

    virtual ~Histograma() {
        if (str_ != NULL) delete []str_;

    }

    char* str() {
        return str_;
    }

    virtual int size() const {
        return AppData::size() + size_;
    }

    void set_string(const char* s) {
        if ((s == NULL) || (*s == 0))
            str_ = NULL, size_ = 0;
        else {
            size_ = strlen(s) + 1;
            str_ = new char[size_];
            assert(str_ != NULL);
            strcpy(str_, s);
        }
    }

    virtual AppData * copy() {
        return new Histograma(*this);
    }
    
    float getAverage(){
        return (sum_ / nleituras_);
    }

    float getBinWidth_() const {
        return binWidth_;
    }

    void setBinWidth_(float binWidth_) {
        this->binWidth_ = binWidth_;
    }

    vector<GenericRow> getBin_array_() const {
        return bin_array_;
    }

    void setBin_array_(vector<GenericRow> bin_array_) {
        this->bin_array_ = bin_array_;
    }

    float getMax_() const {
        return max_;
    }

    void setMax_(float max_) {
        this->max_ = max_;
    }

    float getMin_() const {
        return min_;
    }

    void setMin_(float min_) {
        this->min_ = min_;
    }

    float getSum_() const {
        return sum_;
    }

    void setSum_(float sum_) {
        this->sum_ = sum_;
    }

    float getHeight(int bin) {
        return (float) bin_array_.at(bin).GetOcorrencias();
    }

    int getFreq_prod_() {
        return freq_prod_;
    }

    void setFreq_prod_(int freq_prod_) {
        this->freq_prod_ = freq_prod_;
    }


    int getNleituras_() const {
        return nleituras_;
    }

    void setNleituras_(int nleituras_) {
        this->nleituras_ = nleituras_;
    }

    int getNBins_() const {
        return nBins_;
    }

    void setNBins_(int nBins_) {
        this->nBins_ = nBins_;
    }

    void print(char* sensor = "") {
        vector<GenericRow>::iterator it;
        GenericRow aux;
        printf("**  HISTOGRAMA[%s]  **\n", sensor);
        printf("**  MAX: %f  **\n", max_);
        printf("**  MIN: %f  **\n", min_);
        printf("**  BINWIDTH: %f  **\n", binWidth_);
        printf("**  SUM: %f  **\n", sum_);
        printf("**  NLEITURAS: %d **\n", nleituras_);
        printf("**  NBINS: %d **\n", nBins_);
        printf("**  FREQ_PROD: %d **\n", freq_prod_);
        printf("**  GRAFICO  **\n");
        for (it = bin_array_.begin(); it < bin_array_.end(); it++) {
            aux = *it;
            aux.print();
            //printf("**  BIN[%d][%7.2f|%7.2f]: %d **\n", aux.GetPosicao(), aux.GetInicio(), aux.GetFim(), aux.GetOcorrencias());
        }
        printf("**  FINAL  **\n");
    }

};
////////////////////////////////////////////////////
///// End of HISTOGRAMA Class /////
////////////////////////////////////////////////////



